// MPW TypeScript 实现

import {
  type PasswordTemplate,
  type MPWVersion,
  type MPWTemplates,
  type MPWPassChars,
  type MPWNamespaces,
  type IMPWInstance,
  MPWError,
  MPWValidationError,
  MPWAlgorithmError,
  MPW_VERSIONS
} from './mpw-types';
import { setupSetImmediate, createPBKDF2, createScrypt } from './crypto-utils';

/**
 * MPW 算法常量
 */
const MPW_CONSTANTS = {
  VERSION: 3 as MPWVersion,

  // 命名空间
  NS: "com.lyndir.masterpassword",
  AuthenticationNS: "com.lyndir.masterpassword",
  IdentificationNS: "com.lyndir.masterpassword.login",
  RecoveryNS: "com.lyndir.masterpassword.answer",

  // 密码模板
  templates: {
    maximum: [
      "anoxxxxxxxxxxxxxxxxx",
      "axxxxxxxxxxxxxxxxxno"
    ],
    long: [
      "CvcvnoCvcvCvcv",
      "CvcvCvcvnoCvcv",
      "CvcvCvcvCvcvno",
      "CvccnoCvcvCvcv",
      "CvccCvcvnoCvcv",
      "CvccCvcvCvcvno",
      "CvcvnoCvccCvcv",
      "CvcvCvccnoCvcv",
      "CvcvCvccCvcvno",
      "CvcvnoCvcvCvcc",
      "CvcvCvcvnoCvcc",
      "CvcvCvcvCvccno",
      "CvccnoCvccCvcv",
      "CvccCvccnoCvcv",
      "CvccCvccCvcvno",
      "CvcvnoCvccCvcc",
      "CvcvCvccnoCvcc",
      "CvcvCvccCvccno",
      "CvccnoCvcvCvcc",
      "CvccCvcvnoCvcc",
      "CvccCvcvCvccno"
    ],
    medium: [
      "CvcnoCvc",
      "CvcCvcno"
    ],
    basic: [
      "aaanaaan",
      "aannaaan",
      "aaannaaa"
    ],
    short: [
      "Cvcn"
    ],
    pin: [
      "nnnn"
    ],
    name: [
      "cvccvcvcv"
    ],
    phrase: [
      "cvcc cvc cvccvcv cvc",
      "cvc cvccvcvcv cvcv",
      "cv cvccv cvc cvcvccv"
    ]
  } as MPWTemplates,

  // 字符映射
  passchars: {
    V: "AEIOU",
    C: "BCDFGHJKLMNPQRSTVWXYZ",
    v: "aeiou",
    c: "bcdfghjklmnpqrstvwxyz",
    A: "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
    a: "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
    n: "0123456789",
    o: "@&%?,=[]_:-+*$#!'^~;()/.",
    x: "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
    " ": " "
  } as MPWPassChars
} as const;

/**
 * MPW 实例类
 */
class MPWInstance implements IMPWInstance {
  public readonly name: string;
  public readonly version: MPWVersion;
  public readonly key: Promise<CryptoKey | Uint8Array>;

  constructor(name: string, password: string, version: MPWVersion = MPW_CONSTANTS.VERSION) {
    // 验证输入
    if (!name || !name.length) {
      throw new MPWValidationError("User name is required", "name");
    }

    if (!password || !password.length) {
      throw new MPWValidationError("Master password is required", "password");
    }

    if (!MPW.isValidVersion(version)) {
      throw new MPWValidationError(`Unsupported algorithm version: ${version}`, "version");
    }

    this.name = name;
    this.version = version;

    // 计算主密钥
    if (version >= 0 && version <= MPW_CONSTANTS.VERSION) {
      this.key = MPW.calculateKey(name, password, version);
    } else {
      this.key = Promise.reject(new MPWAlgorithmError(`Algorithm version ${version} not implemented`, version));
    }
  }

  /**
   * 计算种子
   */
  async calculateSeed(
    site: string,
    counter: number = 1,
    context: string | null = null,
    NS: string = MPW_CONSTANTS.NS
  ): Promise<Uint8Array> {
    if (!site) {
      throw new MPWValidationError("Site name is required", "site");
    }

    if (counter < 1 || counter > 4294967295) {
      throw new MPWValidationError("Counter must be between 1 and 4294967295", "counter");
    }

    try {
      // 缓存字符长度（用于旧版本兼容）
      const siteCharLength = site.length;

      // 编码字符串
      const siteBytes = MPW.txtencoder.encode(site);
      const NSBytes = MPW.txtencoder.encode(NS);
      const contextBytes = context ? MPW.txtencoder.encode(context) : null;

      // 创建数据数组
      const dataLength = NSBytes.length + 4 + siteBytes.length + 4 +
                        (contextBytes ? 4 + contextBytes.length : 0);
      const data = new Uint8Array(dataLength);
      const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);

      let offset = 0;

      // 设置命名空间
      data.set(NSBytes, offset);
      offset += NSBytes.length;

      // 设置站点长度
      if (this.version < 2) {
        dataView.setUint32(offset, siteCharLength, false);
      } else {
        dataView.setUint32(offset, siteBytes.length, false);
      }
      offset += 4;

      // 设置站点名称
      data.set(siteBytes, offset);
      offset += siteBytes.length;

      // 设置计数器
      dataView.setInt32(offset, counter, false);
      offset += 4;

      // 设置上下文（如果有）
      if (contextBytes) {
        dataView.setUint32(offset, contextBytes.length, false);
        offset += 4;
        data.set(contextBytes, offset);
      }

      // 使用 HMAC-SHA256 计算种子
      const key = await this.key;

      if (window.crypto && window.crypto.subtle && key instanceof CryptoKey) {
        const signature = await window.crypto.subtle.sign({
          name: "HMAC",
          hash: { name: "SHA-256" }
        }, key, data);

        return new Uint8Array(signature);
      } else if (key instanceof Uint8Array) {
        // 使用 crypto-js 作为回退
        if (!window.CryptoJS) {
          throw new MPWError("No cryptographic implementation available");
        }

        const dataWords = window.CryptoJS.lib.WordArray.create(data);
        const keyWords = window.CryptoJS.lib.WordArray.create(key);
        const hash = window.CryptoJS.HmacSHA256(dataWords, keyWords);

        const seed = new Uint8Array(hash.words.length * 4);
        const seedView = new DataView(seed.buffer, seed.byteOffset, seed.byteLength);

        for (let i = 0; i < hash.words.length; i++) {
          seedView.setInt32(i * 4, hash.words[i], false);
        }

        return seed;
      } else {
        throw new MPWError("Invalid key type");
      }
    } catch (error) {
      if (error instanceof MPWError) {
        throw error;
      }
      throw new MPWError(`Failed to calculate seed: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  /**
   * 生成密码
   */
  async generate(
    site: string,
    counter: number = 1,
    context: string | null = null,
    template: PasswordTemplate = "long",
    NS: string = MPW_CONSTANTS.NS
  ): Promise<string> {
    // 验证模板
    if (!MPW.isValidTemplate(template)) {
      throw new MPWValidationError(`Invalid template: ${template}`, "template");
    }

    // 计算种子
    let seed = await this.calculateSeed(site, counter, context, NS);

    // 版本 0 的特殊处理
    if (this.version < 1) {
      const seedWords = new Uint16Array(seed.length);
      for (let i = 0; i < seed.length; i++) {
        seedWords[i] = (seed[i] > 127 ? 0x00ff : 0x0000) | (seed[i] << 8);
      }
      seed = new Uint8Array(seedWords.buffer);
    }

    // 选择模板
    const templateArray = MPW_CONSTANTS.templates[template];
    const selectedTemplate = templateArray[seed[0] % templateArray.length];

    // 生成密码
    return selectedTemplate.split("").map((char, index) => {
      const chars = MPW_CONSTANTS.passchars[char as keyof MPWPassChars];
      if (!chars) {
        throw new MPWError(`Invalid template character: ${char}`);
      }
      return chars[seed[index + 1] % chars.length];
    }).join("");
  }

  /**
   * 生成认证密码
   */
  async generateAuthentication(
    site: string,
    counter: number = 1,
    context: string = "",
    template: PasswordTemplate = "long"
  ): Promise<string> {
    return this.generate(site, counter, context, template, MPW_CONSTANTS.AuthenticationNS);
  }

  /**
   * 生成身份标识
   */
  async generateIdentification(
    site: string,
    counter: number = 1,
    context: string = "",
    template: PasswordTemplate = "name"
  ): Promise<string> {
    return this.generate(site, counter, context, template, MPW_CONSTANTS.IdentificationNS);
  }

  /**
   * 生成恢复答案
   */
  async generateRecovery(
    site: string,
    counter: number = 1,
    context: string = "",
    template: PasswordTemplate = "phrase"
  ): Promise<string> {
    return this.generate(site, counter, context, template, MPW_CONSTANTS.RecoveryNS);
  }

  // 已弃用的方法（保持向后兼容）
  async generatePassword(
    site: string,
    counter: number = 1,
    template: PasswordTemplate = "long"
  ): Promise<string> {
    return this.generate(site, counter, null, template, MPW_CONSTANTS.AuthenticationNS);
  }

  async generateLogin(
    site: string,
    counter: number = 1,
    template: PasswordTemplate = "name"
  ): Promise<string> {
    return this.generate(site, counter, null, template, MPW_CONSTANTS.IdentificationNS);
  }

  async generateAnswer(
    site: string,
    counter: number = 1,
    context: string = "",
    template: PasswordTemplate = "phrase"
  ): Promise<string> {
    return this.generate(site, counter, context, template, MPW_CONSTANTS.RecoveryNS);
  }

  /**
   * 使实例无效
   */
  invalidate(): void {
    (this as any).key = Promise.reject(new MPWError("MPW instance has been invalidated"));
  }
}

/**
 * MPW 主类
 */
export class MPW {
  // 静态属性
  public static readonly VERSION: MPWVersion = MPW_CONSTANTS.VERSION;
  public static readonly txtencoder: TextEncoder = new TextEncoder();
  public static readonly templates: MPWTemplates = MPW_CONSTANTS.templates;
  public static readonly passchars: MPWPassChars = MPW_CONSTANTS.passchars;
  public static readonly namespaces: MPWNamespaces = {
    NS: MPW_CONSTANTS.NS,
    AuthenticationNS: MPW_CONSTANTS.AuthenticationNS,
    IdentificationNS: MPW_CONSTANTS.IdentificationNS,
    RecoveryNS: MPW_CONSTANTS.RecoveryNS,
    PasswordNS: MPW_CONSTANTS.AuthenticationNS, // 已弃用
    LoginNS: MPW_CONSTANTS.IdentificationNS, // 已弃用
    AnswerNS: MPW_CONSTANTS.RecoveryNS // 已弃用
  };

  /**
   * 构造函数
   */
  constructor(name: string, password: string, version?: MPWVersion) {
    return new MPWInstance(name, password, version) as any;
  }

  /**
   * 计算主密钥
   */
  public static async calculateKey(
    name: string,
    password: string,
    version: MPWVersion = MPW_CONSTANTS.VERSION
  ): Promise<CryptoKey | Uint8Array> {
    if (!name || !name.length) {
      throw new MPWValidationError("User name is required", "name");
    }

    if (!password || !password.length) {
      throw new MPWValidationError("Master password is required", "password");
    }

    if (!MPW.isValidVersion(version)) {
      throw new MPWValidationError(`Unsupported algorithm version: ${version}`, "version");
    }

    try {
      // 缓存字符长度（用于旧版本兼容）
      const nameCharLength = name.length;

      // 编码字符串
      const passwordBytes = MPW.txtencoder.encode(password);
      const nameBytes = MPW.txtencoder.encode(name);
      const NSBytes = MPW.txtencoder.encode(MPW_CONSTANTS.NS);

      // 创建盐
      const salt = new Uint8Array(NSBytes.length + 4 + nameBytes.length);
      const saltView = new DataView(salt.buffer, salt.byteOffset, salt.byteLength);
      let offset = 0;

      // 设置命名空间
      salt.set(NSBytes, offset);
      offset += NSBytes.length;

      // 设置名称长度
      if (version < 3) {
        saltView.setUint32(offset, nameCharLength, false);
      } else {
        saltView.setUint32(offset, nameBytes.length, false);
      }
      offset += 4;

      // 设置名称
      salt.set(nameBytes, offset);

      // 确保加密函数可用
      if (!window.scrypt) {
        window.scrypt = createScrypt();
      }
      if (!window.pbkdf2) {
        window.pbkdf2 = createPBKDF2();
      }

      // 使用 scrypt 派生密钥
      const keyBytes = await window.scrypt(passwordBytes, salt, 32768, 8, 2, 64);

      // 如果支持 Web Crypto API，导入密钥
      if (window.crypto && window.crypto.subtle) {
        try {
          return await window.crypto.subtle.importKey("raw", keyBytes, {
            name: "HMAC",
            hash: { name: "SHA-256" }
          }, false, ["sign"]);
        } catch (error) {
          // 如果导入失败，返回原始字节
          return keyBytes;
        }
      } else {
        return keyBytes;
      }
    } catch (error) {
      if (error instanceof MPWError) {
        throw error;
      }
      throw new MPWError(`Failed to calculate key: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  /**
   * 自测试
   */
  public static async test(): Promise<void> {
    try {
      const mpw = new MPW("user", "password");
      const password = await (mpw as any).generate("example.com", 1, null, "long", MPW_CONSTANTS.NS);

      const expectedPassword = "ZedaFaxcZaso9*";
      if (password !== expectedPassword) {
        throw new MPWError(`Self-test failed; expected: ${expectedPassword}; got: ${password}`);
      }

      console.log("MPW self-test passed");
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Unknown error';
      throw new MPWError(`Self-test failed: ${message}`);
    }
  }

  /**
   * 验证模板是否有效
   */
  public static isValidTemplate(template: string): template is PasswordTemplate {
    return template in MPW_CONSTANTS.templates;
  }

  /**
   * 验证版本是否有效
   */
  public static isValidVersion(version: number): version is MPWVersion {
    return MPW_VERSIONS.includes(version as MPWVersion);
  }

  /**
   * 获取所有模板名称
   */
  public static getTemplateNames(): PasswordTemplate[] {
    return Object.keys(MPW_CONSTANTS.templates) as PasswordTemplate[];
  }
}

/**
 * 初始化函数
 */
export function initializeMPW(): void {
  // 设置 setImmediate polyfill
  setupSetImmediate();

  // 设置加密函数
  if (!window.pbkdf2) {
    window.pbkdf2 = createPBKDF2();
  }

  if (!window.scrypt) {
    window.scrypt = createScrypt();
  }

  // 将 MPW 类添加到全局对象（如果需要）
  if (typeof window !== 'undefined') {
    (window as any).MPW = MPW;
  }
}

// 默认导出
export default MPW;

// 导出类型
export * from './mpw-types';
